Index: lib/moodlelib.php =================================================================== RCS file: /cvsroot/moodle/moodle/lib/moodlelib.php,v retrieving revision 1.910 diff -c -r1.910 moodlelib.php *** lib/moodlelib.php 18 Jul 2007 05:17:47 -0000 1.910 --- lib/moodlelib.php 31 Jul 2007 04:19:09 -0000 *************** *** 7132,7136 **** --- 7132,7167 ---- return in_array($var, $class_vars); } + /** + * Returns an array without repeated objects. + * This function is similar to array_unique, but for arrays that have objects as values + * + * @param unknown_type $array + * @param unknown_type $keep_key_assoc + * @return unknown + */ + function object_array_unique($array, $keep_key_assoc = true) + { + $duplicate_keys = array(); + $tmp = array(); + + foreach ($array as $key=>$val) + { + // convert objects to arrays, in_array() does not support objects + if (is_object($val)) + $val = (array)$val; + + if (!in_array($val, $tmp)) + $tmp[] = $val; + else + $duplicate_keys[] = $key; + } + + foreach ($duplicate_keys as $key) + unset($array[$key]); + + return $keep_key_assoc ? $array : array_values($array); + } + // vim:autoindent:expandtab:shiftwidth=4:tabstop=4:tw=140: ?> Index: lib/db/access.php =================================================================== RCS file: /cvsroot/moodle/moodle/lib/db/access.php,v retrieving revision 1.62 diff -c -r1.62 access.php *** lib/db/access.php 23 Jul 2007 19:50:22 -0000 1.62 --- lib/db/access.php 31 Jul 2007 04:19:14 -0000 *************** *** 1041,1046 **** --- 1041,1079 ---- 'admin' => CAP_ALLOW ) ), + + 'moodle/tag:managetags' => array( + 'captype' => 'write', + 'contextlevel' => CONTEXT_SYSTEM, + 'legacy' => array( + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'admin' => CAP_ALLOW + ) + ), + + 'moodle/tag:createtags' => array( + 'captype' => 'write', + 'contextlevel' => CONTEXT_SYSTEM, + 'legacy' => array( + 'student' => CAP_ALLOW, + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'admin' => CAP_ALLOW + ) + ), + + 'moodle/tag:edittags' => array( + 'captype' => 'write', + 'contextlevel' => CONTEXT_SYSTEM, + 'legacy' => array( + 'student' => CAP_ALLOW, + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'admin' => CAP_ALLOW + ) + ) + ); ?> Index: lib/db/install.xml =================================================================== RCS file: /cvsroot/moodle/moodle/lib/db/install.xml,v retrieving revision 1.96 diff -c -r1.96 install.xml *** lib/db/install.xml 31 Jul 2007 02:28:52 -0000 1.96 --- lib/db/install.xml 31 Jul 2007 04:19:28 -0000 *************** *** 1595,1600 **** --- 1595,1645 ---- + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+ + + + + + + + + + + + + +
Index: user/edit.php =================================================================== RCS file: /cvsroot/moodle/moodle/user/edit.php,v retrieving revision 1.163 diff -c -r1.163 edit.php *** user/edit.php 11 Jul 2007 09:05:17 -0000 1.163 --- user/edit.php 31 Jul 2007 04:19:30 -0000 *************** *** 1,10 **** ! libdir.'/gdlib.php'); require_once($CFG->dirroot.'/user/edit_form.php'); require_once($CFG->dirroot.'/user/editlib.php'); require_once($CFG->dirroot.'/user/profile/lib.php'); httpsrequired(); --- 1,11 ---- ! libdir.'/gdlib.php'); require_once($CFG->dirroot.'/user/edit_form.php'); require_once($CFG->dirroot.'/user/editlib.php'); require_once($CFG->dirroot.'/user/profile/lib.php'); + require_once($CFG->dirroot.'/tag/lib.php'); httpsrequired(); *************** *** 35,40 **** --- 36,45 ---- error('User ID was incorrect'); } + //user interests separated by commas + if( !empty($CFG->usetags)) { + $user->interests = tag_names_csv(get_item_tags('user',$userid)); + } // remote users cannot be edited if (is_mnet_remote_user($user)) { redirect($CFG->wwwroot . "/user/view.php?course={$course->id}"); *************** *** 65,70 **** --- 70,76 ---- //Load custom profile fields data profile_load_data($user); + //create form $userform = new user_edit_form(); $userform->set_data($user); *************** *** 90,96 **** //update preferences useredit_update_user_preference($usernew); ! //update user picture if (!empty($CFG->gdversion) and empty($CFG->disableuserimages)) { useredit_update_picture($usernew, $userform); --- 96,107 ---- //update preferences useredit_update_user_preference($usernew); ! ! //update interests ! if( !empty($CFG->usetags)) { ! useredit_update_interests($usernew, $usernew->interests); ! } ! //update user picture if (!empty($CFG->gdversion) and empty($CFG->disableuserimages)) { useredit_update_picture($usernew, $userform); Index: user/view.php =================================================================== RCS file: /cvsroot/moodle/moodle/user/view.php,v retrieving revision 1.158 diff -c -r1.158 view.php *** user/view.php 23 Jul 2007 16:47:42 -0000 1.158 --- user/view.php 31 Jul 2007 04:19:31 -0000 *************** *** 1,9 **** ! dirroot.'/user/profile/lib.php'); $id = optional_param('id', 0, PARAM_INT); // user id $course = optional_param('course', SITEID, PARAM_INT); // course id (defaults to Site) $enable = optional_param('enable', ''); // enable email --- 1,11 ---- ! dirroot.'/user/profile/lib.php'); + require_once($CFG->dirroot.'/tag/lib.php'); + $id = optional_param('id', 0, PARAM_INT); // user id $course = optional_param('course', SITEID, PARAM_INT); // course id (defaults to Site) $enable = optional_param('enable', ''); // enable email *************** *** 371,376 **** --- 373,390 ---- } /// End of printing groups + /// Printing Interests + if( !empty($CFG->usetags)) { + $interests = get_item_tags('user', $user->id); + + $instereststr = tag_links_csv($interests); + + if ($interests) { + print_row(get_string('interests').": ",rtrim($instereststr)); + } + } + /// End of Printing Interests + echo ""; echo ""; Index: user/editlib.php =================================================================== RCS file: /cvsroot/moodle/moodle/user/editlib.php,v retrieving revision 1.7 diff -c -r1.7 editlib.php *** user/editlib.php 21 May 2007 07:37:55 -0000 1.7 --- user/editlib.php 31 Jul 2007 04:19:31 -0000 *************** *** 54,59 **** --- 54,64 ---- } } + function useredit_update_interests($user, $cvs_tag_names) + { + update_item_tags('user', $user->id, $cvs_tag_names); + } + function useredit_shared_definition(&$mform) { global $CFG; *************** *** 216,221 **** --- 221,232 ---- } + if( !empty($CFG->usetags)) { + $mform->addElement('header', 'moodle_interests', get_string('interests')); + $mform->addElement('static', 'helptextinterests', '' , get_string('enteryourinterests')); + $mform->addElement('textarea', 'interests', get_string('interests'), 'cols="45" rows="3"'); + } + /// Moodle optional fields $mform->addElement('header', 'moodle_optional', get_string('optional', 'form')); $mform->setAdvanced('moodle_optional'); *************** *** 255,260 **** --- 266,273 ---- $mform->addElement('text', 'address', get_string('address'), 'maxlength="70" size="25"'); $mform->setType('address', PARAM_MULTILANG); + + } ?> Index: lang/en_utf8/moodle.php =================================================================== RCS file: /cvsroot/moodle/moodle/lang/en_utf8/moodle.php,v retrieving revision 1.122 diff -c -r1.122 moodle.php *** lang/en_utf8/moodle.php 30 Jul 2007 22:56:45 -0000 1.122 --- lang/en_utf8/moodle.php 31 Jul 2007 04:18:50 -0000 *************** *** 552,557 **** --- 552,558 ---- $string['entercourse'] = 'Click to enter this course'; $string['enteremailaddress'] = 'Enter in your email address to reset your password and have the new password sent to you via email.'; + $string['enteryourinterests'] = 'Enter your interests separated by commas'; $string['entries'] = 'Entries'; $string['error'] = 'Error'; $string['errortoomanylogins'] = 'Sorry, you have exceeded the allowed number of login attempts. Restart your browser.'; *************** *** 773,778 **** --- 774,780 ---- $string['info'] = 'Information'; $string['institution'] = 'Institution'; $string['instudentview'] = 'in student view'; + $string['interests'] = 'Interests'; $string['invalidemail'] = 'Invalid email address'; $string['invalidlogin'] = 'Invalid login, please try again'; $string['ip_address'] = 'IP Address'; Index: lang/en_utf8/role.php =================================================================== RCS file: /cvsroot/moodle/moodle/lang/en_utf8/role.php,v retrieving revision 1.39 diff -c -r1.39 role.php *** lang/en_utf8/role.php 24 Jul 2007 09:23:43 -0000 1.39 --- lang/en_utf8/role.php 31 Jul 2007 04:18:50 -0000 *************** *** 128,133 **** --- 128,136 ---- $string['site:viewfullnames'] = 'Always see full names of users'; $string['site:viewparticipants'] = 'View participants'; $string['site:viewreports'] = 'View reports'; + $string['tag:managetags'] = 'Manage tags'; + $string['tag:createtags'] = 'Create tags'; + $string['tag:edittags'] = 'Edit tags'; $string['user:changeownpassword'] = 'Change own password'; $string['user:create'] = 'Create users'; $string['user:delete'] = 'Delete users'; Index: lang/en_utf8/admin.php =================================================================== RCS file: /cvsroot/moodle/moodle/lang/en_utf8/admin.php,v retrieving revision 1.133 diff -c -r1.133 admin.php *** lang/en_utf8/admin.php 27 Jul 2007 06:18:39 -0000 1.133 --- lang/en_utf8/admin.php 31 Jul 2007 04:18:46 -0000 *************** *** 203,208 **** --- 203,209 ---- For example: standard,orangewhite.'; $string['configtimezone'] = 'You can set the default timezone here. This is the only the DEFAULT timezone for displaying dates - each user can override this by setting their own in their profile. \"Server time\" here will make Moodle default to the server\'s operating system setting, but \"Server time\" in the user profile will make the user default to this timezone setting. Cronjobs that depend on a time of day to run will use this timezone.'; $string['configunzip'] = 'Indicate the location of your unzip program (Unix only, optional). If specified, this will be used to unpack zip archives on the server. If you leave this blank, then Moodle will use internal routines.'; + $string['configusetags'] = 'Should tags functionality across the site be enabled?'; $string['configvariables'] = 'Variables'; $string['configwarning'] = 'Be careful modifying these settings - strange values could cause problems.'; $string['configzip'] = 'Indicate the location of your zip program (Unix only, optional). If specified, this will be used to create zip archives on the server. If you leave this blank, then Moodle will use internal routines.'; *************** *** 607,612 **** --- 608,614 ---- $string['userscreated'] = 'Users created'; $string['usersrenamed'] = 'Users renamed'; $string['usersupdated'] = 'Users updated'; + $string['usetags'] = 'Enable tags functionality'; $string['validateerror'] = 'This value was not valid:'; ?> Index: admin/settings/security.php =================================================================== RCS file: /cvsroot/moodle/moodle/admin/settings/security.php,v retrieving revision 1.19 diff -c -r1.19 security.php *** admin/settings/security.php 7 May 2007 11:40:02 -0000 1.19 --- admin/settings/security.php 31 Jul 2007 04:18:43 -0000 *************** *** 37,43 **** 2 => get_string('groupblogs','blog'), 1 => get_string('personalblogs','blog'), 0 => get_string('disableblogs','blog')))); ! $temp->add(new admin_setting_configcheckbox('cronclionly', get_string('cronclionly', 'admin'), get_string('configcronclionly', 'admin'), 0)); $temp->add(new admin_setting_configpasswordunmask('cronremotepassword', get_string('cronremotepassword', 'admin'), get_string('configcronremotepassword', 'admin'), '', PARAM_RAW)); --- 37,43 ---- 2 => get_string('groupblogs','blog'), 1 => get_string('personalblogs','blog'), 0 => get_string('disableblogs','blog')))); ! $temp->add(new admin_setting_configcheckbox('usetags', get_string('usetags','admin'),get_string('configusetags', 'admin'),'1')); $temp->add(new admin_setting_configcheckbox('cronclionly', get_string('cronclionly', 'admin'), get_string('configcronclionly', 'admin'), 0)); $temp->add(new admin_setting_configpasswordunmask('cronremotepassword', get_string('cronremotepassword', 'admin'), get_string('configcronremotepassword', 'admin'), '', PARAM_RAW)); Index: theme/standard/styles_layout.css =================================================================== RCS file: /cvsroot/moodle/moodle/theme/standard/styles_layout.css,v retrieving revision 1.481 diff -c -r1.481 styles_layout.css *** theme/standard/styles_layout.css 30 Jul 2007 22:56:50 -0000 1.481 --- theme/standard/styles_layout.css 31 Jul 2007 04:19:30 -0000 *************** *** 24,29 **** --- 24,30 ---- mymoodle question tabs + tags user various modules *************** *** 2746,2751 **** --- 2747,2959 ---- } /*** + *** Tags + ***/ + + div#tag-description { + width:600px; + padding-top:0px; + margin-top:20px; + margin-left:auto; + margin-right:auto; + } + + div#tag-management-box { + text-align:center; + line-height:20px; + display:block; + font-size:12px; + } + + div#tag-user-table { + width:600px; + margin-left:auto; + margin-right:auto; + display:table; + } + + div.user-table-row { + width:100%; + min-height:150px; + margin-top:20px; + margin-bottom:20px; + margin-left:auto; + margin-right:auto; + text-align:center; + display:block; + } + + div.user-box { + width:150px; + text-align:center; + display:block; + float:left; + } + + img.user-image { + border:0px; + height:100px; + width:100px; + } + + div#small-tag-cloud-box { + width:300px; + margin-left:auto; + margin-right:auto; + margin-bottom:0px; + margin-top:0px; + } + + div#big-tag-cloud-box { + width:600px; + margin-left:auto; + margin-right:auto; + margin-bottom:0px; + margin-top:0px; + display:block; + float:none; + } + + ul#tag-cloud-list { + list-style:none; + padding:5px; + margin:0px; + list-style-type:none; + } + ul#tag-cloud-list li { + margin:0px; + display:inline; + } + + /* search start*/ + + div#tag-search-box { + text-align:center; + margin-left:auto; + margin-right:auto; + margin-top:10px; + margin-bottom:10px; + } + + div#tag-search-results-container { + padding:0px; + width:100%; + } + + ul#tag-search-results { + padding:0px; + margin-left:20%; + margin-right:20%; + margin-top:15px; + margin-bottom:0px; + float:left; + width:60%; + display:block; + list-style:none; + } + + ul#tag-search-results li{ + + width:30%; + float:left; + padding-left:1%; + text-align:left; + line-height:20px; + padding-right:1%; + + } + + div#tags-management-links { + text-align:right; + display:block; + font-size:12px; + + } + /* search end*/ + + /* tag management start*/ + span.flagged-tag { + color:#FF0000; + } + span.flagged-tag a{ + color:#FF0000; + } + + table#tag-management-list { + text-align:left; + margin-left:auto; + margin-right:auto; + } + + table#tag-management-list tr td{ + padding-left:4px; + padding-right :4px; + } + + form#tag-management-form { + text-align:center; + } + /* tag management end*/ + + /* autocomplete start*/ + #relatedtags-autocomplete-container + { + margin-left::auto; + margin-right:auto; + min-height:4.6em; + width:100%; + } + + #relatedtags-autocomplete { + position:relative; + display:block; + width:60%; + margin-left::auto; + margin-right:auto; + } + #relatedtags-autocomplete .yui-ac-content + { + position:absolute; + width:420px; + left:53%; + border:1px solid #404040; + background:#fff; + overflow:hidden; + z-index:9050; + } + #relatedtags-autocomplete .ysearchquery + { + position:absolute; + right:10px; + color:#808080; + z-index:10; + } + #relatedtags-autocomplete .yui-ac-shadow { + position:absolute; + margin:.3em; + width:100%; + background:#a0a0a0; + z-index:9049; + } + #relatedtags-autocomplete ul { + padding:0;width:100%; + margin:0; + list-style-type:none; + } + + #relatedtags-autocomplete li { + padding:0 5px; + cursor:default; + white-space: + nowrap; + } + #relatedtags-autocomplete li.yui-ac-highlight + { + background:#FFFFCC; + } + /* autocomplete end*/ + + /*** *** User ***/ Index: tag/search.php =================================================================== RCS file: tag/search.php diff -N tag/search.php *** /dev/null 1 Jan 1970 00:00:00 -0000 --- tag/search.php 1 Jan 1970 00:00:00 -0000 *************** *** 0 **** --- 1,47 ---- + dirroot.'/lib/weblib.php'); + + global $CFG; + require_login(); + + if( empty($CFG->usetags)) { + error(get_string('tagsaredisabled', 'tag')); + } + + $query = optional_param('query', '', PARAM_TEXT); + $page = optional_param('page', 0, PARAM_INT); // which page to show + $perpage = optional_param('perpage', 18, PARAM_INT); + + $navlinks = array(); + $navlinks[] = array('name' => get_string('tags', 'tag'), 'link' => "{$CFG->wwwroot}/tag/search.php", 'type' => ''); + $navigation = build_navigation($navlinks); + + + print_header_simple(get_string('tags', 'tag'), '', $navigation); + + $systemcontext = get_context_instance(CONTEXT_SYSTEM); + if ( has_capability('moodle/tag:managetags',$systemcontext) ) { + $manage_link = "wwwroot}/tag/manage.php\">" . get_string('managetags', 'tag') . "" ; + print_box($manage_link, '', 'tags-management-links'); + } + + print_heading(get_string('searchtags', 'tag'), '', 2); + + print_tag_search_box(); + + if(!empty($query)) { + print_tag_search_results($query, $page, $perpage); + } + + echo '

'; + + print_box_start('generalbox', 'big-tag-cloud-box'); + print_tag_cloud(rand_tags_count(60), true, 170,70); + print_box_end(); + + print_footer(); + + ?> Index: lang/en_utf8/tag.php =================================================================== RCS file: lang/en_utf8/tag.php diff -N lang/en_utf8/tag.php *** /dev/null 1 Jan 1970 00:00:00 -0000 --- lang/en_utf8/tag.php 1 Jan 1970 00:00:00 -0000 *************** *** 0 **** --- 1,33 ---- + Index: tag/manage.php =================================================================== RCS file: tag/manage.php diff -N tag/manage.php *** /dev/null 1 Jan 1970 00:00:00 -0000 --- tag/manage.php 1 Jan 1970 00:00:00 -0000 *************** *** 0 **** --- 1,57 ---- + dirroot.'/lib/weblib.php'); + + require_login(); + + if( empty($CFG->usetags)) { + error(get_string('tagsaredisabled', 'tag')); + } + + //managing tags requires moodle/tag:managetags capability + $systemcontext = get_context_instance(CONTEXT_SYSTEM); + require_capability('moodle/tag:managetags', $systemcontext); + + $tagschecked = optional_param('tagschecked', array()); + $action = optional_param('action', '', PARAM_ALPHA); + + $navlinks = array(); + $navlinks[] = array('name' => get_string('tags', 'tag'), 'link' => "{$CFG->wwwroot}/tag/search.php", 'type' => ''); + $navlinks[] = array('name' => get_string('managetags', 'tag'), 'link' => '', 'type' => ''); + + $navigation = build_navigation($navlinks); + print_header_simple(get_string('managetags', 'tag'), '', $navigation); + + switch($action) { + + case 'delete': + + $notice = tag_name_from_string(implode($tagschecked, ', ')); + $notice = str_replace(',', ', ', $notice); + $notice .= ' -- ' . get_string('deleted','tag'); + notify($notice , 'green'); + + tag_delete(implode($tagschecked, ',')); + break; + case 'reset': + + $notice = tag_name_from_string(implode($tagschecked, ', ')); + $notice = str_replace(',', ', ', $notice); + $notice .= ' -- ' . get_string('reset','tag'); + notify($notice , 'green'); + + tag_flag_reset(implode($tagschecked, ',')); + break; + } + + echo '
'; + + print_tag_management_list(); + + echo '
'; + + print_footer(); + + ?> Index: user/tag.php =================================================================== RCS file: user/tag.php diff -N user/tag.php *** /dev/null 1 Jan 1970 00:00:00 -0000 --- user/tag.php 1 Jan 1970 00:00:00 -0000 *************** *** 0 **** --- 1,41 ---- + usetags)) { + error('Tags are disabled!'); + } + + switch ($action) { + case 'addinterest': + $id = optional_param('id', 0, PARAM_INT); + $name = optional_param('name', '', PARAM_TEXT); + + if (empty($name) && $id) { + $name = tag_name($id); + } + + tag_an_item('user',$USER->id, $name); + + if (!empty($name) && !$id) { + $id = tag_id($name); + } + + redirect($CFG->wwwroot.'/tag/index.php?id='.$id); + break; + case 'flaginappropriate': + + $id = required_param('id', PARAM_INT); + + tag_flag_inappropriate($id); + + redirect($CFG->wwwroot.'/tag/index.php?id='.$id, get_string('responsiblewillbenotified','tag')); + break; + } + + ?> Index: tag/tag_autocomplete.php =================================================================== RCS file: tag/tag_autocomplete.php diff -N tag/tag_autocomplete.php *** /dev/null 1 Jan 1970 00:00:00 -0000 --- tag/tag_autocomplete.php 1 Jan 1970 00:00:00 -0000 *************** *** 0 **** --- 1,19 ---- + usetags)) { + error(get_string('tagsaredisabled', 'tag')); + } + + $query = optional_param('query', 0, PARAM_TEXT); + $query = tag_normalize($query); + + $similar_tags = similar_tags($query); + + $count = 0; + foreach ($similar_tags as $tag){ + echo $tag->name . "\t" . $tag->id . "\n"; + } + + ?> Index: tag/edit_form.php =================================================================== RCS file: tag/edit_form.php diff -N tag/edit_form.php *** /dev/null 1 Jan 1970 00:00:00 -0000 --- tag/edit_form.php 1 Jan 1970 00:00:00 -0000 *************** *** 0 **** --- 1,33 ---- + dirroot.'/lib/formslib.php'); + + class tag_edit_form extends moodleform { + + function definition () { + + $mform =& $this->_form; + + $mform->addElement('header', 'tag', get_string('description','tag')); + + $mform->addElement('hidden', 'id'); + + $mform->addElement('htmleditor', 'description', get_string('description', 'tag'), array('rows'=>20)); + $mform->setType('description', PARAM_CLEANHTML); + + $mform->addElement('format', 'descriptionformat', get_string('format')); + + $mform->addElement('html', '
'); + $mform->addElement('textarea', 'relatedtags', get_string('relatedtags','tag'), 'cols="50" rows="3"'); + $mform->setType('relatedtags', PARAM_MULTILANG); + $mform->addElement('html', '
'); + $mform->addElement('html', '
'); + + + $this->add_action_buttons(false, get_string('updatetag', 'tag')); + + } + + } + + ?> Index: tag/index.php =================================================================== RCS file: tag/index.php diff -N tag/index.php *** /dev/null 1 Jan 1970 00:00:00 -0000 --- tag/index.php 1 Jan 1970 00:00:00 -0000 *************** *** 0 **** --- 1,67 ---- + dirroot.'/lib/weblib.php'); + + require_login(); + + if( empty($CFG->usetags)) { + error(get_string('tagsaredisabled', 'tag')); + } + + $tagid = optional_param('id', 0, PARAM_INT); // tag id + $userpage = optional_param('userpage', 0, PARAM_INT); // which page to show + $perpage = optional_param('perpage', 16, PARAM_INT); + + $tag = tag_by_id($tagid); + + if(!$tag) { + redirect($CFG->wwwroot.'/tag/search.php'); + } + + $tagname = mb_convert_case($tag->name, MB_CASE_TITLE, "UTF-8"); + + $navlinks = array(); + $navlinks[] = array('name' => get_string('tags', 'tag'), 'link' => "{$CFG->wwwroot}/tag/search.php", 'type' => ''); + $navlinks[] = array('name' => $tagname, 'link' => '', 'type' => ''); + + $navigation = build_navigation($navlinks); + print_header_simple(get_string('tag', 'tag') . ' - ' . $tagname, '', $navigation); + + $systemcontext = get_context_instance(CONTEXT_SYSTEM); + if ($tag->flag > 0 && has_capability('moodle/tag:managetags', $systemcontext)) { + $tagname = '' . $tagname . ''; + } + + print_heading($tagname, '', 2); + + print_tag_management_box($tag); + + print_tag_description_box($tag); + + + $usercount = count_items_tagged_with($tagid,'user'); + + if ($usercount > 0) { + + print_heading(get_string('userstaggedwith', 'tag') . ' ' . $tagname . ': ' . $usercount, '', 3); + + $baseurl = $CFG->wwwroot.'/tag/index.php?id=' . $tagid; + + print_paging_bar($usercount, $userpage, $perpage, $baseurl.'&', 'userpage'); + + print_tagged_users_table($tag, 4, $userpage * $perpage, $perpage); + + + } + //print_box_start('generalbox', 'small-tag-cloud-box'); + // print_tag_cloud(15,170,80); + //print_box_end(); + + print_footer(); + + + + + ?> Index: tag/lib.php =================================================================== RCS file: tag/lib.php diff -N tag/lib.php *** /dev/null 1 Jan 1970 00:00:00 -0000 --- tag/lib.php 1 Jan 1970 00:00:00 -0000 *************** *** 0 **** --- 1,1461 ---- + tagtype = $tag_type; + $tag_object->userid = $USER->id; + + $systemcontext = get_context_instance(CONTEXT_SYSTEM); + $can_create_tags = has_capability('moodle/tag:createtags',$systemcontext); + + foreach ($tags as $tag_name) { + + $tag_object->name = $tag_name; + $tag_object->timemodified = time(); + + // if ( !record_exists('tag', 'name', $tag_name) && !empty($tag_name) && !is_numeric($tag_name) ) { + if ( $can_create_tags && !empty($tag_name) && !is_numeric($tag_name) ) { + insert_record('tag', $tag_object); + } + } + + return tags_id($normalized_tag_names_csv); + + } + + /** + * Deletes tags + * + * Ex 1: tag_delete('a very cool tag, another nice tag') + * Will delete the tags with names 'a very cool tag' and 'another nice tag' from the 'tags' table, if they exist! + * + * Ex 2: tag_delete('computers, 123, 143, algorithms') + * Will delete tags with names 'computers' and 'algorithms' and tags with ids 123 and 143. + * + * + * @param string $tag_names_or_ids_csv **normalized** tag names or ids of the tags to be deleted. + */ + + function tag_delete($tag_names_or_ids_csv) { + + //covert all ids to names + $tag_names_csv = tag_name_from_string($tag_names_or_ids_csv); + + //put apostrophes in names + $tag_names_csv_with_apos = "'" . str_replace(',', "','", $tag_names_csv) . "'"; + + delete_records_select('tag',"name IN ($tag_names_csv_with_apos)"); + + } + + /** + * Get all tags from the records + * + * @param string $tag_types_csv (optional, default value is "default". If '*' is passed, tags of any type will be returned). + * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter). + * @param string $fields a comma separated list of fields to return + * (optional, by default 'id, tagtype, name'). The first field will be used as key for the + * array so must be a unique field such as 'id'. + */ + function get_all_tags($tag_types_csv="default", $sort, $fields = 'id, tagtype, name') { + + if ($tag_types_csv == '*'){ + return get_records('tag', '', '', $sort, $fields); + } + + $tag_types_csv_with_apos = "'" . str_replace(',', "','", $tag_types_csv ) . "'"; + + return get_records_list('tag', 'tagtype', $tag_types_csv_with_apos, $sort, $fields); + } + + /** + * Determines if a tag exists + * + * @param string $tag_name_or_id **normalized** tag name, or an id. + * @return true if exists or false otherwise + * + */ + function tag_exists($tag_name_or_id) { + + if (is_numeric($tag_name_or_id)) { + return record_exists('tag', 'id', $tag_name_or_id); + } + elseif (is_string($tag_name_or_id)) { + return record_exists('tag', 'name', $tag_name_or_id); + } + } + + /** + * Function that returns the id of a tag + * + * @param String $tag_name **normalized** name of the tag + * @return int id of the matching tag + */ + function tag_id($tag_name) { + $tag = get_record('tag', 'name', trim($tag_name), '', '', '', '', 'id'); + return $tag->id; + } + + /** + * Function that returns the ids of tags + * + * Ex: tags_id('computers, algorithms') + * + * @param String $tag_names_csv comma separated **normalized** tag names. + * @return Array array with the tags ids, indexed by their **normalized** names + */ + function tags_id($tag_names_csv) { + + $normalized_tag_names_csv = tag_normalize($tag_names_csv); + $tag_names_csv_with_apos = "'" . str_replace(',', "','", $normalized_tag_names_csv ) . "'"; + + $tag_objects = get_records_list('tag','name', $tag_names_csv_with_apos, "" , "name, id" ); + + $tags_ids = array(); + foreach ($tag_objects as $tag) { + $tags_ids[$tag->name] = $tag->id; + } + + return $tags_ids; + } + + /** + * Function that returns the name of a tag + * + * @param int $tag_id id of the tag + * @return String name of the tag with the id passed + */ + function tag_name($tag_id) { + $tag = get_record('tag', 'id', $tag_id, '', '', '', '', 'name'); + return $tag->name; + } + + /** + * Function that retrieves the names of tags given their ids + * + * @param String $tag_ids_csv comma separated tag ids + * @return Array an array with the tags names, indexed by their ids + */ + + function tags_name($tag_ids_csv) { + + $tag_ids_csv_with_apos = "'" . str_replace(',', "','", $tag_ids_csv ) . "'"; + + $tag_objects = get_records_list('tag','id', $tag_ids_csv_with_apos, "" , "name, id" ); + + $tags_names = array(); + foreach ($tag_objects as $tag) { + $tags_names[$tag->id] = $tag->name; + } + + return $tags_names; + } + + /** + * Function that retrieves a tag object by its id + * + * @param String $tag_id + * @return mixed a fieldset object containing the first matching record, or false if none found + */ + function tag_by_id($tag_id) { + + return get_record('tag','id',$tag_id); + } + + /** + * Function that retrieves a tag object by its name + * + * @param String $tag_name + * @return mixed a fieldset object containing the first matching record, or false if none found + */ + function tag_by_name($tag_name) { + $tag = get_record('tag','name',$tag_name); + return $tag; + } + + /** + * Returns comma separated ids of tags given a string with comma separated names or ids of tags + * + * Ex: tag_id_from_string('moodle, 12, education, 33, 11') + * might return the string '10,12,22,33,11' + * + * This is a helper function used by functions of this API to process function arguments ($tag_name_or_id) + * + * @param string $tag_names_or_ids_csv comma separated **normalized** names or ids of tags + * @return int comma separated ids of the tags + */ + function tag_id_from_string($tag_names_or_ids_csv) { + + $tag_names_or_ids = explode(',', $tag_names_or_ids_csv); + + $tag_ids = array(); + foreach ($tag_names_or_ids as $name_or_id) { + + if (is_numeric($name_or_id)){ + $tag_ids[] = $name_or_id; + } + elseif (is_string($name_or_id)) { + $tag_ids[] = tag_id( $name_or_id ); + } + + } + + $tag_ids_csv = implode(',',$tag_ids); + $tag_ids_csv = str_replace(' ', '', $tag_ids_csv); + + return rtrim($tag_ids_csv, ','); + } + + /** + * Returns comma separated **normalized** names of tags given a string with comma separated names or ids of tags + * + * Ex: tag_name_from_string('mOOdle, 12, eduCAtIOn, 33, 11') + * might return the string 'moodle,computers,education,algorithms,software' + * + * This is a helper function used by functions of this API to process function arguments ($tag_name_or_id) + * + * @param string $tag_names_or_ids_csv comma separated **normalized** names or ids of tags + * @return int comma separated ids of the tags + */ + function tag_name_from_string($tag_names_or_ids_csv) { + + $tag_names_or_ids = explode(',', $tag_names_or_ids_csv); + + $tag_names = array(); + foreach ($tag_names_or_ids as $name_or_id) { + + if (is_numeric($name_or_id)){ + $tag_names[] = tag_name($name_or_id); + } + elseif (is_string($name_or_id)) { + $tag_names[] = tag_normalize($name_or_id); + } + + } + + $tag_names_csv = implode(',',$tag_names); + + return rtrim($tag_names_csv, ','); + + } + + /** + * Associates a tag with an item + * + * Ex 1: tag_an_item('user', '1', 'hisTOrY, RELIGIONS, roman' ) + * This will tag an user whose id is 1 with "history", "religions", "roman" + * If the tag names passed do not exist, they will get created. + * + * Ex 2: tag_an_item('user', '1', 'hisTory, 12, 11, roman') + * This will tag an user whose id is 1 with 'history', 'roman' and with tags of ids 12 and 11 + * + * @param string $item_type name of the table where the item is stored. Ex: 'user' + * @param string $item_id id of the item to be tagged + * @param string $tag_names_or_ids_csv comma separated tag names (can be unormalized) or ids of existing tags + * @param string $tag_type type of the tags that are beeing added (optional, default value is "default") + */ + + function tag_an_item($item_type, $item_id, $tag_names_or_ids_csv, $tag_type="default") { + + $norm_tag_names_or_ids_csv = tag_normalize($tag_names_or_ids_csv); + + //convert any tag ids passed to their corresponding tag names + $tag_names_csv = tag_name_from_string($norm_tag_names_or_ids_csv); + + //create the tags + $tags_created_ids = tag_create($tag_names_csv,$tag_type); + + $tag_instance = new StdClass; + $tag_instance->itemtype = $item_type; + $tag_instance->itemid = $item_id; + + //create tag instances + foreach ($tags_created_ids as $tag_id) { + $tag_instance->tagid = $tag_id; + insert_record('tag_instance',$tag_instance); + } + + + // update_tag_correlations($item_type, $item_id); + + } + + + /** + * Updates the tags associated with an item + * + * Ex 1: + * Suppose user 1 is tagged only with "algorithms", "computers" and "software" + * By calling update_item_tags('user', 1, 'algorithms, software, mathematics') + * User 1 will now be tagged only with "algorithms", "software" and "mathematics" + * + * Ex 2: + * update_item_tags('user', '1', 'algorithms, 12, 13') + * User 1 will now be tagged only with "algorithms", and with tags of ids 12 and 13 + * + * + * @param string $item_type name of the table where the item is stored. Ex: 'user' + * @param string $item_id id of the item to be tagged + * @param string $tag_names_or_ids_csv comma separated tag names (can be unormalized) or ids of existing tags + * @param string $tag_type type of the tags that are beeing added (optional, default value is "default") + */ + + function update_item_tags($item_type, $item_id, $tag_names_or_ids_csv, $tag_type="default") { + + //if $tag_names_csv is an empty string, remove all tag associations of the item + if( empty($tag_names_or_ids_csv) ){ + untag_an_item($item_type, $item_id); + return; + } + + //normalize tags names + $norm_tag_names_or_ids_csv = tag_normalize($tag_names_or_ids_csv); + + //convert any tag ids passed to their corresponding tag names + $tag_names_csv = tag_name_from_string($norm_tag_names_or_ids_csv); + + //associate the tags passed with the item + tag_an_item($item_type, $item_id, $tag_names_csv, $tag_type ); + + //get the ids of the tags passed + $existing_and_new_tags_ids = tags_id( $tag_names_csv ); + + // delete any tag instance with $item_type and $item_id + // that are not in $tag_names_csv + $tags_id_csv = "'" . implode("','", $existing_and_new_tags_ids) . "'" ; + + $select = " + itemid = '{$item_id}' + AND + itemtype = '{$item_type}' + AND + tagid NOT IN ({$tags_id_csv}) + "; + + delete_records_select('tag_instance', $select); + + } + + /** + * Removes the association of an item with a tag + * + * Ex: untag_an_item('user', '1', 'history, 11, roman' ) + * The user with id 1 will no longer be tagged with 'history', 'roman' and the tag of id 11 + * Calling untag_an_item('user','1') will remove all tags associated with user 1. + * + * @param string $item_type name of the table where the item is stored. Ex: 'user' + * @param string $item_id id of the item to be untagged + * @param string $tag_names_or_ids_csv comma separated tag **normalized** names or ids of existing tags (optional, + * if none is given, all tags of the item will be removed) + */ + + function untag_an_item($item_type, $item_id, $tag_names_or_ids_csv='') { + + if ($tag_names_or_ids_csv == ""){ + + delete_records('tag_instance','itemtype', $item_type, 'itemid', $item_id); + + } + else { + + $tag_ids_csv = tag_id_from_string($norm_tag_names_or_ids_csv); + + $tag_ids_csv_with_apos = "'" . str_replace(',', "','", $tag_ids_csv ) . "'"; + + delete_records_select('tag_instance', + "tagid IN ($tags_id_csv_with_apos) AND itemtype='$item_type' AND itemid='$item_id'"); + } + + //update_tag_correlations($item_type, $item_id); + + } + + /** + * Function that gets the tags that are associated with an item + * + * Ex: get_item_tags('user', '1') + * + * @param string $item_type name of the table where the item is stored. Ex: 'user' + * @param string $item_id id of the item beeing queried + * @param string $fields tag fields to be selected (optional, default is 'id, name, tagtype') + * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set). + * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set). + * @return mixed an array of objects, or false if no records were found or an error occured. + */ + + function get_item_tags($item_type, $item_id, $fields='id, name, tagtype, flag', $limitfrom='', $limitnum='') { + + global $CFG; + + $fields = 'tg.' . $fields; + $fields = str_replace(',', ',tg.', $fields); + + $query = " + SELECT + {$fields} + FROM + {$CFG->prefix}tag_instance ti + INNER JOIN + {$CFG->prefix}tag tg + ON + tg.id = ti.tagid + WHERE + ti.itemtype = '{$item_type}' AND + ti.itemid = '{$item_id}'"; + + return get_records_sql($query, $limitfrom, $limitnum); + + } + + + + /** + * Function that returns the items of a certain type associated with a certain tag + * + * Ex 1: get_items_tagged_with('user', 'banana') + * Ex 2: get_items_tagged_with('user', '11') + * + * @param string $item_type name of the table where the item is stored. Ex: 'user' + * @param string $tag_name_or_id is a single **normalized** tag name or the id of a tag + * @param string $sort an order to sort the results in (optional, a valid SQL ORDER BY parameter). + * (to avoid field name ambiguity in the query, use the identifier "it" Ex: 'it.name ASC' ) + * @param string $fields a comma separated list of fields to return + * (optional, by default all fields are returned). The first field will be used as key for the + * array so must be a unique field such as 'id'. + * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set). + * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set). + * @return mixed an array of objects indexed by their ids, or false if no records were found or an error occured. + */ + + function get_items_tagged_with($item_type, $tag_name_or_id, $sort='', $fields='*', $limitfrom='', $limitnum='') { + + global $CFG; + + $tag_id = tag_id_from_string($tag_name_or_id); + + $fields = 'it.' . $fields; + $fields = str_replace(',', ',it.', $fields); + + if ($sort) { + $sort = ' ORDER BY '. $sort; + } + + $query = " + SELECT + {$fields} + FROM + {$CFG->prefix}{$item_type} it + INNER JOIN + {$CFG->prefix}tag_instance tt + ON + it.id = tt.itemid + WHERE + tt.itemtype = '{$item_type}' AND + tt.tagid = '{$tag_id}' + {$sort} + "; + + + return get_records_sql($query, $limitfrom, $limitnum); + + } + + /** + * Returns the number of items tagged with a tag + * + * @param string $tag_name_or_id is a single **normalized** tag name or the id of a tag + * @param string $item_type name of the table where the item is stored. Ex: 'user' (optional, if none is set any + * type will be counted) + * @return int the count. If an error occurrs, 0 is returned. + */ + function count_items_tagged_with($tag_name_or_id, $item_type='') { + + global $CFG; + + $tag_id = tag_id_from_string($tag_name_or_id); + + if (empty($item_type)){ + $query = " + SELECT + COUNT(*) AS count + FROM + {$CFG->prefix}tag_instance tt + WHERE + tagid = {$tag_id}"; + } + else + { + $query = " + SELECT + COUNT(*) AS count + FROM + {$CFG->prefix}{$item_type} it + INNER JOIN + {$CFG->prefix}tag_instance tt + ON + it.id = tt.itemid + WHERE + tt.itemtype = '{$item_type}' AND + tt.tagid = '{$tag_id}' "; + } + + + return count_records_sql($query); + + } + + + /** + * Determines if an item is tagged with a certain tag + * + * @param string $item_type name of the table where the item is stored. Ex: 'user' + * @param string $item_id id of the item beeing queried + * @param string $tag_name_or_id is a single **normalized** tag name or the id of a tag + * @return bool true if a matching record exists, else false. + */ + function is_item_tagged_with($item_type,$item_id, $tag_name_or_id) { + + $tag_id = tag_id_from_string($tag_name_or_id); + + return record_exists('tag_instance','itemtype',$item_type,'itemid',$item_id, 'tagid', $tag_id); + } + + /** + * Search for tags with names that match some text + * + * @param string $text string that the tag names will be matched against + * @param boolean $ordered If true, tags are ordered by their popularity. If false, no ordering. + * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set). + * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set). + * @return mixed an array of objects, or false if no records were found or an error occured. + */ + + function search_tags($text, $ordered=true, $limitfrom='' , $limitnum='' ) { + + global $CFG; + + $text = tag_normalize($text); + + if ($ordered) { + $query = " + SELECT + tg.id, tg.name, COUNT(ti.id) AS count + FROM + {$CFG->prefix}tag tg + LEFT JOIN + {$CFG->prefix}tag_instance ti + ON + tg.id = ti.tagid + WHERE + tg.name + LIKE + '%{$text}%' + GROUP BY + tg.id + ORDER BY + count + DESC"; + } else { + $query = " + SELECT + tg.id, tg.name + FROM + {$CFG->prefix}tag tg + WHERE + tg.name + LIKE + '%{$text}%' + "; + } + + + return get_records_sql($query, $limitfrom , $limitnum); + + } + + /** + * Function that returns tags that start with some text + * + * @param string $text string that the tag names will be matched against + * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set). + * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set). + * @return mixed an array of objects, or false if no records were found or an error occured. + */ + function similar_tags($text, $limitfrom='' , $limitnum='' ) { + + global $CFG; + + $text = tag_normalize($text); + + $query = " + SELECT + * + FROM + {$CFG->prefix}tag t + WHERE + t.name + LIKE + '{$text}%' + "; + + return get_records_sql($query, $limitfrom , $limitnum); + } + + /** + * Returns tags related to a tag + * + * Related tags of a tag come from two sources: + * - manually added related tags, which are tag_instance entries for that tag + * - correlated tags, which are a calculated + * + * @param string $tag_name_or_id is a single **normalized** tag name or the id of a tag + * @param int $limitnum return a subset comprising this many records (optional, default is 10) + * @return mixed an array of tag objects + */ + + function related_tags($tag_name_or_id, $limitnum=10) { + + $tag_id = tag_id_from_string($tag_name_or_id); + + //gets the manually added related tags + $manual_related_tags = get_item_tags('tag',$tag_id); + if ($manual_related_tags == false) $manual_related_tags = array(); + + //gets the correlated tags + $automatic_related_tags = correlated_tags($tag_id); + if ($automatic_related_tags == false) $automatic_related_tags = array(); + + $related_tags = array_merge($manual_related_tags,$automatic_related_tags); + + return array_slice( object_array_unique($related_tags) , 0 , $limitnum ); + + + } + + /** + * Returns the correlated tags of a tag + * The correlated tags are retrieved from the tag_correlation table, which is a caching table. + * + * @param string $tag_name_or_id is a single **normalized** tag name or the id of a tag + * @return mixed an array of tag objects + */ + function correlated_tags($tag_name_or_id) { + + $tag_id = tag_id_from_string($tag_name_or_id); + + $tag_correlation = get_record('tag_correlation','tagid',$tag_id); + + $tags_id_csv_with_apos = stripcslashes($tag_correlation->correlatedtags); + + return get_records_select('tag', "id IN ({$tags_id_csv_with_apos})", '', 'id, name, tagtype'); + } + + /** + * Recalculates tag correlations of all the tags associated with an item + * This function could be called whenever the tags associations with an item changes + * ( for example when tag_an_item() or untag_an_item() is called ) + * + * @param string $item_type name of the table where the item is stored. Ex: 'user' + * @param string $item_id id of the item + */ + function update_tag_correlations($item_type, $item_id) { + + $item_tags = get_item_tags($item_type, $item_id); + + foreach ($item_tags as $tag) { + cache_correlated_tags($tag->id); + } + } + + /** + * Calculates and stores the correlated tags of a tag. + * The correlations are stored in the 'tag_correlation' table. + * + * Two tags are correlated if they appear together a lot. + * Ex.: Users tagged with "computers" will probably also be tagged with "algorithms". + * + * The rationale for the 'tag_correlation' table is performance. + * It works as a cache for a potentially heavy load query done at the 'tag_instance' table. + * So, the 'tag_correlation' table stores redundant information derived from the 'tag_instance' table. + * + * @param string $tag_name_or_id is a single **normalized** tag name or the id of a tag + * @param number $min_correlation cutoff percentage (optional, default is 0.25) + * @param int $limitnum return a subset comprising this many records (optional, default is 10) + */ + function cache_correlated_tags($tag_name_or_id, $min_correlation=0.25, $limitnum=10) { + + global $CFG; + + $tag_id = tag_id_from_string($tag_name_or_id); + + // query that counts how many times any tag appears together in items + // with the tag passed as argument ($tag_id) + $query = + " SELECT + tb.tagid , COUNT(*) nr + FROM + {$CFG->prefix}tag_instance ta + INNER JOIN + {$CFG->prefix}tag_instance tb + ON + ta.itemid = tb.itemid + WHERE + ta.tagid = {$tag_id} + GROUP BY + tb.tagid + ORDER BY + nr DESC"; + + $tag_correlations = get_records_sql($query, 0, $limitnum); + + $tags_id_csv_with_apos = "'"; + $cutoff = $tag_correlations[$tag_id]->nr * $min_correlation; + + foreach($tag_correlations as $correlation) { + if($correlation->nr >= $cutoff && $correlation->tagid != $tag_id ){ + $tags_id_csv_with_apos .= $correlation->tagid."','"; + } + } + $tags_id_csv_with_apos = substr($tags_id_csv_with_apos,0,-2); + + + //saves correlation info in the caching table + + $tag_correlation_obj = get_record('tag_correlation','tagid',$tag_id); + + if ($tag_correlation_obj) { + $tag_correlation_obj->correlatedtags = addslashes($tags_id_csv_with_apos); + update_record('tag_correlation',$tag_correlation_obj); + } + else { + $tag_correlation_obj = new StdClass; + $tag_correlation_obj->tagid = $tag_id; + $tag_correlation_obj->correlatedtags = addslashes($tags_id_csv_with_apos); + insert_record('tag_correlation',$tag_correlation_obj); + } + + + } + + /** + * This function cleans up the 'tag_instance' table + * It removes orphans in 'tag_instances' table + * + */ + function tag_instance_table_cleanup() { + + global $CFG; + + //get the itemtypes present in the 'tag_instance' table + $query = " + SELECT + DISTINCT(itemtype) + FROM + {$CFG->prefix}tag_instance + "; + + $items_types = get_records_sql($query); + + // for each itemtype, remove tag_instances that are orphans + // That is: For a given tag_instance, if in the itemtype table there's no entry with id equal to itemid, + // then this tag_instance is an orphan and it will be removed. + foreach ($items_types as $type) { + + $query = " + {$CFG->prefix}tag_instance.id + IN + ( SELECT sq1.id + FROM + (SELECT sq2.* + FROM {$CFG->prefix}tag_instance sq2 + LEFT JOIN {$CFG->prefix}{$type->itemtype} item + ON sq2.itemid = item.id + WHERE item.id IS NULL + AND sq2.itemtype = '{$type->itemtype}') + sq1 + ) "; + + delete_records_select('tag_instance', $query); + } + + // remove tag_instances that are orphans because tagid does not correspond to an + // existing tag + $query = " + {$CFG->prefix}tag_instance.id + IN + (SELECT sq1.id + FROM + (SELECT sq2.* + FROM {$CFG->prefix}tag_instance sq2 + LEFT JOIN {$CFG->prefix}tag tg + ON sq2.tagid = tg.id + WHERE tg.id IS NULL ) + sq1 + ) + "; + + delete_records_select('tag_instance', $query); + } + + + /** + * Function that normalizes a tag name + * + * Ex: tag_normalize('bANAana') -> returns 'banana' + * tag_normalize('lots of spaces') -> returns 'lots of spaces' + * tag_normalize('%!%!% non alpha numeric %!%!%') -> returns 'non alpha numeric' + * tag_normalize('tag one, TAG TWO, TAG three, and anotheR tag') + * -> returns 'tag one,tag two,tag three,and another tag' + * + * @param string $tag_names_csv unnormalized CSV tag names + * @return string **normalized** CSV tag names + */ + + function tag_normalize($tag_names_csv) { + + $tags = explode(',', $tag_names_csv); + + if(sizeof($tags) > 1) { + + foreach ($tags as &$tag) { + $tag = tag_normalize($tag); + } + + return implode(',' , $tags); + + } + + // only one tag was passed + else { + // value is converted to lowercase and all non-alphanumeric caracters are removed + //$value = preg_replace('|[^\w ]|i', '', strtolower(trim($tag_names_csv))); + $value = preg_replace('|[\!\@\#\$\%\^\&\*\(\)\-\+\=\~\`\.\[\]\{\}\:\;\\\/\<\>\|]|i', '', moodle_strtolower(trim($tag_names_csv))); + + //removes excess white spaces + $value = preg_replace('/\s\s+/', ' ', $value); + + return $value; + } + + } + + function is_tag_clean($tag_names_csv) + { + + } + + function tag_flag_inappropriate($tag_names_or_ids_csv){ + + $tag_ids_csv = tag_id_from_string($tag_names_or_ids_csv); + + $tag_ids = explode(',', $tag_ids_csv); + + foreach ($tag_ids as $id){ + $tag = get_record('tag','id',$id, '', '', '', '', 'id,flag'); + + $tag->flag++; + $tag->timemodified = time(); + + update_record('tag', $tag); + } + } + + function tag_flag_reset($tag_names_or_ids_csv){ + + global $CFG; + + $tag_ids_csv = tag_id_from_string($tag_names_or_ids_csv); + + $tag_ids_csv_with_apos = "'" . str_replace(',', "','", $tag_ids_csv) . "'"; + + $timemodified = time(); + + $query = " + UPDATE + {$CFG->prefix}tag tg + SET + tg.flag = 0, + tg.timemodified = {$timemodified} + WHERE + tg.id + IN + ({$tag_ids_csv_with_apos}) + "; + + execute_sql($query, false); + } + + /** + * Function that returns comma separated HTML links to the tag pages of the tags passed + * + * @param array $tag_objects an array of tag objects + * @return string CSV, HTML links to tag pages + */ + + function tag_links_csv($tag_objects) { + + global $CFG; + $tag_links = ''; + + $systemcontext = get_context_instance(CONTEXT_SYSTEM); + $can_manage_tags = has_capability('moodle/tag:managetags', $systemcontext); + + foreach ($tag_objects as $tag){ + //highlight tags that have been flagged as inappropriate for those who can manage them + $tagname = $tag->name; + if ($tag->flag > 0 && $can_manage_tags) { + $tagname = '' . $tagname . ''; + } + $tag_links .= ' '.$tagname.','; + } + + return rtrim($tag_links, ','); + } + + /** + * Function that returns comma separated names of the tags passed + * Example of string that might be returned: 'history, wars, greek history' + * + * @param array $tag_objects + * @return string CSV tag names + */ + + function tag_names_csv($tag_objects) { + + $tag_names = ''; + + foreach ($tag_objects as $tag){ + $tag_names .= $tag->name.', '; + } + + return rtrim($tag_names, ', '); + } + + + /** + * Returns a number of random tags, ordered by their popularity + * + * @param int $nr_of_tags number of random tags to be returned + * @param unknown_type $tag_type + * @return mixed an array of tag objects with the following fields: id, name and count + */ + function rand_tags_count($nr_of_tags=20, $tag_type = 'default') { + + global $CFG; + + $tags = get_all_tags($tag_type); + + if(sizeof($tags) < $nr_of_tags) { + $nr_of_tags = sizeof($tags); + } + + $rndtags = array_rand($tags, $nr_of_tags); + + $tags_id_csv_with_apos = "'"; + foreach($rndtags as $tagid) { + $tags_id_csv_with_apos .= $tags[$tagid]->id . "','"; + } + $tags_id_csv_with_apos = substr($tags_id_csv_with_apos,0,-2); + + + $query = " + SELECT + tg.id, tg.name, COUNT(ti.id) AS count, tg.flag + FROM + {$CFG->prefix}tag_instance ti + INNER JOIN + {$CFG->prefix}tag tg + ON + tg.id = ti.tagid + WHERE + ti.tagid + IN + ({$tags_id_csv_with_apos}) + GROUP BY + tagid + ORDER BY + count + ASC"; + + return get_records_sql($query); + + + } + + /*-------------------- Printing functions -------------------- */ + + /** + * Prints a box that contains the management links of a tag + * + * @param $tag_object + */ + + function print_tag_management_box($tag_object) { + + global $USER, $CFG; + + $tagname = /*ucwords*/($tag_object->name); + + print_box_start('box','tag-management-box'); + + $systemcontext = get_context_instance(CONTEXT_SYSTEM); + + $addtaglink = ''; + if ( has_capability('moodle/tag:managetags',$systemcontext) ) { + $manage_link = "wwwroot}/tag/manage.php\">" . get_string('managetags', 'tag') . "" ; + echo $manage_link .' | '; + } + + // if the user is not tagged with the $tag_object tag, a link "add blahblah to my interests" will appear + if( !is_item_tagged_with('user', $USER->id, $tag_object->id )) { + $addtaglink = ''; + $addtaglink .= get_string('addtagtomyinterests','tag',$tagname). ''; + echo $addtaglink .' | '; + } + + // only people with moodle/tag:edittags capability may edit the tag description + if ( has_capability('moodle/tag:edittags',$systemcontext) && is_item_tagged_with('user', $USER->id, $tag_object->id ) ) { + echo ' Edit this tag | '; + } + + // flag as inappropriate link + $flagtaglink = ''; + $flagtaglink .= get_string('flagasinappropriate','tag',$tagname). ''; + echo $flagtaglink; + + print_box_end(); + + } + + /** + * Prints a box with the description of a tag and its related tags + * + * @param unknown_type $tag_object + */ + + function print_tag_description_box($tag_object) { + + global $USER, $CFG; + + $tagname = /*ucwords*/($tag_object->name); + $related_tags = related_tags($tag_object->id); //get_item_tags('tags',$tag_object->id); + + + print_box_start('generalbox', 'tag-description'); + + if (!empty($tag_object->description)) { + echo format_text($tag_object->description, $tag_object->descriptionformat ); + } + else { + echo format_text(get_string('thistaghasnodesc','tag')); + } + + + if ($related_tags) { + echo '
'.get_string('relatedtags','tag').': ' . tag_links_csv($related_tags); + } + + print_box_end(); + } + + /** + * Prints a table of the users tagged with the tag passed as argument + * + * @param $tag_object + * @param int $users_per_row number of users per row to display + * @param int $limitfrom prints users starting at this point (optional, required if $limitnum is set). + * @param int $limitnum prints this many users (optional, required if $limitfrom is set). + */ + + function print_tagged_users_table($tag_object, $users_per_row='5', $limitfrom='' , $limitnum='') { + + //List of users with this tag + $userlist = array_values( get_items_tagged_with( + 'user', + $tag_object->id, + 'lastaccess DESC' , + 'id, firstname, lastname, picture', + $limitfrom, + $limitnum) ); + + //user table box + print_box_start('generalbox', 'tag-user-table'); + + print_user_table($userlist, $users_per_row); + + print_box_end(); + //end table box + + + + } + + /** + * Prints a table of users + * + * @param array $userlist an array of user objects + * @param int $users_per_row number of users per row to display + */ + function print_user_table($userlist, $users_per_row='5') { + + $nrofrows = ceil( count($userlist) / $users_per_row ); + for($row = 0; $row < $nrofrows; $row++){ + + //table row box + print_box_start('user-table-row', 'row_'.$row); + + for($col = 0; ($col < $users_per_row) ; $col++ ) { + + $index = $row * $users_per_row + $col; + + if ($index < count($userlist) ) { + + print_user_box( $userlist[$index] ); + + } + + } + + print_box_end(); + //end table row box + + } + + } + + /** + * Prints an individual user box + * + * @param $user user object (contains the following fields: id, firstname, lastname and picture) + */ + function print_user_box($user) { + + global $CFG; + + $usercontext = get_context_instance(CONTEXT_USER, $user->id); + + $profilelink = ''; + if ( has_capability('moodle/user:viewdetails', $usercontext) ) { + $profilelink = $CFG->wwwroot.'/user/view.php?id='.$user->id; + } + + print_box_start('user-box', $user->id); + + if (!empty($profilelink)) echo ''; + + //print user image + if ($user->picture) { + echo ''; + } + else { + echo ''; + } + + echo '
'; + + if (!empty($profilelink)) echo '
'; + + $fullname = fullname($user); + //truncate name if it's too big + if (strlen($fullname) > 26) $fullname = substr($fullname,0,26) . '...'; + + echo '' . $fullname . ''; + + print_box_end(); + + } + + /** + * Prints the tag search box + * + */ + function print_tag_search_box() { + + global $CFG; + + print_box_start('','tag-search-box'); + + echo '
'; + echo ''; + echo '
'; + echo '
'; + + print_box_end(); + } + + /** + * Prints the tag search results + * + * @param string $query text that tag names will be matched against + * @param int $page current page + * @param int $perpage nr of users displayed per page + */ + function print_tag_search_results($query, $page, $perpage) { + + global $CFG, $USER; + + $count = sizeof( search_tags($query,false) ); + $tags = array_values(search_tags($query, true, $page * $perpage , $perpage)); + + $baseurl = $CFG->wwwroot.'/tag/search.php?query=' . $query; + + // link "Add $query to my interests" + $addtaglink = ''; + if( !is_item_tagged_with('user', $USER->id, $query )) { + $addtaglink = ''; + $addtaglink .= get_string('addtagtomyinterests','tag',$query). ''; + } + + + if($tags) { // there are results to display!! + + print_heading(get_string('searchresultsfor', 'tag') . " \"{$query}\" : {$count}", '', 3); + + //print a link "Add $query to my interests" + if (!empty($addtaglink)) { + print_box($addtaglink,'box','tag-management-box'); + } + + $nr_of_lis_per_ul = 6; + $nr_of_uls = ceil( sizeof($tags) / $nr_of_lis_per_ul); + + echo '
    '; + for($i = 0; $i < $nr_of_uls; $i++) { + echo '
  • '; + foreach (array_slice($tags, $i * $nr_of_lis_per_ul, $nr_of_lis_per_ul ) as $tag) { + $tag_link = ' '.$tag->name.''; + echo '•' . $tag_link . '
    '; + } + echo '
  • '; + } + echo '
'; + echo '
 
'; // <-- small layout hack in order to look good in Firefox + + print_paging_bar($count, $page, $perpage, $baseurl.'&', 'page'); + } + else { //no results were found!! + + print_heading(get_string('noresultsfor', 'tag') . " \"{$query}\" ", '', 3); + + //print a link "Add $query to my interests" + if (!empty($addtaglink)) { + print_box($addtaglink,'box','tag-management-box'); + } + + } + + + } + + /** + * Prints a tag cloud + * + * @param int $nr_of_tags number of tags in the cloud + * @param int $max_size maximum text size, in percentage + * @param int $min_size minimum text size, in percentage + */ + function print_tag_cloud($tag_objects, $shuffle=true, $max_size=180, $min_size=80) { + + global $CFG; + + $tagcloud = $tag_objects; + + if ( $shuffle ) { + shuffle($tagcloud); + } + + $count = array(); + foreach ($tagcloud as $key => $value){ + if(!empty($value->count)) { + $count[$key] = log10($value->count); + } + else{ + $count[$key] = 0; + } + } + + $max = max($count); + $min = min($count); + + $spread = $max - $min; + if (0 == $spread) { // we don't want to divide by zero + $spread = 1; + } + + $step = ($max_size - $min_size)/($spread); + + $systemcontext = get_context_instance(CONTEXT_SYSTEM); + $can_manage_tags = has_capability('moodle/tag:managetags', $systemcontext); + + //prints the tag cloud + echo '
    '; + foreach ($tagcloud as $key => $tag) { + + $size = $min_size + ((log10($tag->count) - $min) * $step); + $size = ceil($size); + + $style = 'style="font-size: '.$size.'%"'; + $title = 'title="'.$tag->count . ' ' .get_string('thingstaggedwith','tag') . ' ' . $tag->name.'"'; + $href = 'href="'.$CFG->wwwroot.'/tag/index.php?id='.$tag->id.'"'; + + //highlight tags that have been flagged as inappropriate for those who can manage them + $tagname = $tag->name; + if ($tag->flag > 0 && $can_manage_tags) { + $tagname = '' . $tag->name . ''; + } + + $tag_link = '
  • '.$tagname.'
  • '; + + echo $tag_link; + + } + echo '
'; + + } + + function print_tag_management_list($perpage='100') { + + global $CFG, $USER; + require_once($CFG->libdir.'/tablelib.php'); + + //setup table + $tablecolumns = array('name', 'owner', 'count', 'flag', 'timemodified', ''); + $tableheaders = array( get_string('name' , 'tag'), + get_string('owner','tag'), + get_string('count','tag'), + get_string('flag','tag'), + get_string('timemodified','tag'), + get_string('select', 'tag') + ); + + $table = new flexible_table('tag-management-list-'.$USER->id); + + $baseurl = $CFG->wwwroot.'/tag/manage.php'; + + $table->define_columns($tablecolumns); + $table->define_headers($tableheaders); + $table->define_baseurl($baseurl); + + $table->sortable(true, 'flag', SORT_DESC); + + $table->set_attribute('cellspacing', '0'); + $table->set_attribute('id', 'tag-management-list'); + $table->set_attribute('class', 'generaltable generalbox'); + + $table->set_control_variables(array( + TABLE_VAR_SORT => 'ssort', + TABLE_VAR_HIDE => 'shide', + TABLE_VAR_SHOW => 'sshow', + TABLE_VAR_IFIRST => 'sifirst', + TABLE_VAR_ILAST => 'silast', + TABLE_VAR_PAGE => 'spage' + )); + + $table->setup(); + + if ($table->get_sql_sort()) { + $sort = ' ORDER BY '.$table->get_sql_sort(); + } else { + $sort = ''; + } + + if ($table->get_sql_where()) { + $where = "WHERE {$table->get_sql_where()}"; + } else { + $where = ''; + } + + $query = " + SELECT + tg.id, tg.name, COUNT(ti.id) AS count, u.id AS owner, tg.flag, tg.timemodified + FROM + {$CFG->prefix}tag_instance ti + RIGHT JOIN + {$CFG->prefix}tag tg + ON + tg.id = ti.tagid + LEFT JOIN + {$CFG->prefix}user u + ON + tg.userid = u.id + {$where} + GROUP BY + tg.id + {$sort} + "; + + $totalcount = count_records('tag'); + + $table->initialbars($totalcount > $perpage); + $table->pagesize($perpage, $totalcount); + + //retrieve tags from DB + $taglist = array_values( get_records_sql($query, $table->get_page_start(), $table->get_page_size()) ); + + // print_tag_cloud(array_values(get_records_sql($query)), false); + + //populate table with data + foreach ($taglist as $tag ){ + + $name = ''.$tag->name.''; + $owner = '' . $tag->owner . ''; + $count = $tag->count; + $flag = $tag->flag; + $timemodified = format_time(time() - $tag->timemodified); + $checkbox = ''; + + //if the tag if flagged, highlight it + if ($tag->flag > 0) { + $name = '' . $name . ''; + $owner = '' . $owner . ''; + $count = '' . $count . ''; + $flag = '' . $flag . ''; + $timemodified = '' . $timemodified . ''; + } + + $data = array($name , $owner ,$count ,$flag, $timemodified, $checkbox); + + $table->add_data($data); + } + + + echo '
'; + + + echo ' '; + echo ' '; + echo '

'; + echo ''; + + echo ''; + + $table->print_html(); + + echo '
'; + } + + ?> Index: tag/edit.php =================================================================== RCS file: tag/edit.php diff -N tag/edit.php *** /dev/null 1 Jan 1970 00:00:00 -0000 --- tag/edit.php 1 Jan 1970 00:00:00 -0000 *************** *** 0 **** --- 1,90 ---- + dirroot.'/lib/weblib.php'); + + require_login(); + + if( empty($CFG->usetags)) { + error(get_string('tagsaredisabled', 'tag')); + } + + $tagid = required_param('id', PARAM_INT); // user id + $tag = tag_by_id($tagid); + $tagname = /*ucwords*/($tag->name); + + //Editing a tag requires moodle/tag:edittags capability + $systemcontext = get_context_instance(CONTEXT_SYSTEM); + require_capability('moodle/tag:edittags', $systemcontext); + + // set the relatedtags field of the $tag object that will be passed to the form + $tag->relatedtags = tag_names_csv( get_item_tags('tag',$tagid) ); + + $tagform = new tag_edit_form(); + $tagform->set_data($tag); + + // if new data has been sent, update the tag record + if ($tagnew = $tagform->get_data()) { + + $tagnew->timemodified = time(); + + if (!update_record('tag', $tagnew)) { + error('Error updating tag record'); + } + + //updated related tags + update_item_tags('tag', $tagnew->id, $tagnew->relatedtags); + + redirect($CFG->wwwroot.'/tag/index.php?id='.$tagnew->id); + } + + + $navlinks = array(); + $navlinks[] = array('name' => get_string('tags', 'tag'), 'link' => "{$CFG->wwwroot}/tag/search.php", 'type' => ''); + $navlinks[] = array('name' => $tagname, 'link' => '', 'type' => ''); + + $navigation = build_navigation($navlinks); + print_header_simple(get_string('tag', 'tag') . ' - '. $tagname, '', $navigation); + + print_heading($tagname, '', 2); + + $tagform->display(); + + + ?> + + + + + + + + + + + + + + + +